package club.cearnach.web.controller.movie;

import club.cearnach.api.dto.HotMovieDTO;
import club.cearnach.api.exception.MovieException;
import club.cearnach.api.exception.UserException;
import club.cearnach.api.exception.UserFavoriteException;
import club.cearnach.api.form.PageBean;
import club.cearnach.api.service.*;
import club.cearnach.api.service.impl.MovieServiceImpl;
import club.cearnach.api.vo.movie.MovieIndexVO;
import club.cearnach.api.vo.movie.MovieVO;
import club.cearnach.core.property.MovieWebProperties;
import club.cearnach.core.util.StringUtil;
import club.cearnach.entity.*;
import club.cearnach.web.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

/**
 * @author 阮胜
 * @date 2018/5/7 15:36
 */
@Controller
@Slf4j
@RequestMapping("/movie")
@Api(tags = "前台电影接口")
public class MovieController extends BaseController {
    public static final String MOVIE_INDEX = "movie/index";
    /**
     * 首页默认要显示的热门电影数量
     */
    private static final int HOT_MOVIE_DISPLAY_COUNT = 10;
    @Autowired
    private MovieWebProperties movieWebProperties;
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private CacheManager cacheManager;
    private final INewsService newsService;
    private final IUserRateService userRateService;
    private final IUserService userService;
    private final IUserFavoriteService userFavoriteService;

    public MovieController(IActorService actorService, IAdminService adminService, IDirectorService directorService, IMovieLanguageService movieLanguageService, IMovieService movieService, IMovieTypeService movieTypeService, IPersonService personService, IPlaceService placeService, IVisitorService visitorService, INewsService newsService, IUserRateService userRateService, IUserService userService, IUserFavoriteService userFavoriteService) {
        super(actorService, adminService, directorService, movieLanguageService, movieService, movieTypeService, personService, placeService, visitorService);
        this.newsService = newsService;
        this.userRateService = userRateService;
        this.userService = userService;
        this.userFavoriteService = userFavoriteService;
    }

    @GetMapping("/index")
    @ApiOperation(value = "电影前台首页")
    public Callable<String> index(Model model) {
        return () -> {
            MovieIndexVO movieIndexVO = new MovieIndexVO();
            PageBean pageBean = new PageBean(0, 10);
            Page<Movie> newMoviePage = movieService.findAllOrderByCreateDateDesc(pageBean);
            Page<Movie> rateMoviePage = movieService.findAllOrderByRateDesc(pageBean);
            List<HotMovieDTO> hotMovieList = obtainHotMovie(pageBean);
            Page<MovieType> movieTypePage = movieTypeService.findAllOrderByCreateDate(new PageBean(0, 5));
            List<MovieType> movieTypeList = movieTypePage.getContent()
                    .stream()
                    .filter(movieType -> movieService.countMovieByTypeId(movieType.getId()) > 0)
                    .collect(Collectors.toList());
            Map<Integer, List<Movie>> movieCategoryList = movieTypeList.stream()
                    .collect(Collectors.toMap(MovieType::getId
                            , movieType -> movieService.findByTypeId(movieType.getId())));
            List<Actor> actorList = actorService.findAllByOrderByUpdateDateDesc(pageBean)
                    .stream()
                    .filter(actor -> actor.getPerson() != null && actor.getPerson().getId() != null)
                    .collect(Collectors.toList());
            movieIndexVO.setActorList(actorList);
            movieIndexVO.setMovieCategoryMap(movieCategoryList);
            movieIndexVO.setMovieTypeList(movieTypeList);
            movieIndexVO.setHighRateMovieList(rateMoviePage.getContent());
            movieIndexVO.setNewMovieList(newMoviePage.getContent());
            movieIndexVO.setHighRateMovieList(rateMoviePage.getContent());
            movieIndexVO.setHotMovieDTOList(hotMovieList);
            movieIndexVO.setNews(newsService.findLastNews());
            model.addAttribute(movieIndexVO);
            return "movie/index";
        };

    }

    @GetMapping("/detail/{id}")
    public String detail(@PathVariable("id") Integer movieId, Model model) throws MovieException, UserException {
        cacheManager.getCache(MovieServiceImpl.CACHE_MOVIE_NAME).clear();
        Movie movie = movieService.findById(movieId);
        MovieVO movieVO = new MovieVO();
        BeanUtils.copyProperties(movie, movieVO);
        String actors = StringUtil.entityCollection2String(movie.getActors(), "/");
        String directors = StringUtil.entityCollection2String(movie.getDirectors(), "/");
        movieVO.setActors(actors);
        movieVO.setDirectors(directors);
        movieVO.setLanguage(movie.getMovieLanguage().getLanguage());
        movieVO.setTypeName(movie.getType().getTypeName());
        model.addAttribute(movieVO);
        model.addAttribute("isFavorite", isFavorite(movieId));
        movieService.incrementMovieHot(movieId);
        return "movie/movie-detail";
    }

    private boolean isFavorite(Integer movieId) {
        User user = obtainUserAccount();
        if (user == null) {
            return false;
        }
        try {
            userFavoriteService.findByUserIdAndMovieId(user.getId(), movieId);
        } catch (UserFavoriteException e) {
            return false;
        }
        return true;

    }

    private User obtainUserAccount() {
        String account = SecurityContextHolder.getContext().getAuthentication().getName();
        return userService.findByAccount(account).orElse(null);

    }

    /**
     * 获取热门电影,如果热门电影数量低于10个,那么插入10条最新电影
     *
     * @return
     */
    private List<HotMovieDTO> obtainHotMovie(PageBean pageBean) {
        List<HotMovieDTO> hotMovieDTOList = movieService.findHotMovie(10);
        if (CollectionUtils.isEmpty(hotMovieDTOList)) {
            hotMovieDTOList = new ArrayList<>();
        }
        if (hotMovieDTOList.size() < HOT_MOVIE_DISPLAY_COUNT) {
            List<HotMovieDTO> movieDTOList = movieService.findAllOrderByCreateDateDesc(pageBean)
                    .getContent()
                    .stream()
                    .map(movie -> {
                        HotMovieDTO hotMovieDTO = new HotMovieDTO();
                        Movie m = new Movie();
                        BeanUtils.copyProperties(movie, m);
                        hotMovieDTO.setMovie(m);
                        hotMovieDTO.setHot(0);
                        return hotMovieDTO;
                    }).collect(Collectors.toList());
            hotMovieDTOList.addAll(movieDTOList);
        }

        return hotMovieDTOList;
    }

    @ApiOperation("前台自动转发页面")
    @GetMapping("/*")
    public void everyPage() {

    }


    /**
     * 获取图片
     *
     * @param resp
     * @param fileName
     */
    @GetMapping("/image/{fileName}")
    public void image(HttpServletResponse resp, @PathVariable("fileName") String fileName) {
        transferFile(resp, movieWebProperties.getUpload().getMovieImagePath(), fileName);
    }

    @GetMapping("/image/person/{fileName}")
    public void personImage(HttpServletResponse resp, @PathVariable("fileName") String fileName) {
        transferFile(resp, movieWebProperties.getUpload().getPersonImagePath(), fileName);
    }

    @GetMapping("/video/{fileName}")
    public void video(HttpServletResponse resp, @PathVariable("fileName") String fileName) {
        transferFile(resp, movieWebProperties.getUpload().getMovieVideoPath(), fileName);
    }

    private void transferFile(HttpServletResponse resp, String filePath, String fileName) {
        String filePathAndName = filePath + fileName;
        File file = new File(filePathAndName);
        if (file.exists()) {
            try (ServletOutputStream os = resp.getOutputStream(); FileInputStream fis = new FileInputStream(file)) {
                IOUtils.copy(fis, os);
            } catch (IOException e) {
            }
        }
    }

    @GetMapping("/list/grid")
    public String listGrid(Model model) {
        List<MovieType> typeList = movieTypeService.findAll();
        model.addAttribute("movieCount", movieService.count());
        model.addAttribute(typeList);
        return "movie/movie-list-grid";
    }

    @GetMapping("/list/grid/fragment")
    public String listGridFragment(PageBean pageBean, Integer typeId, Model model) {
        if (typeId == null || typeId == 0) {
            List<MovieType> typeList = movieTypeService.findAll(PageRequest.of(0, 1)).getContent();
            if (!CollectionUtils.isEmpty(typeList)) {
                typeId = typeList.get(0).getId();
            }
        }
        Page<Movie> moviePage = movieService.findAllByTypeIdOrderByCreateDateDesc(typeId, pageBean);
        List<Movie> movieList = moviePage.getContent();
        model.addAttribute(movieList);
        model.addAttribute("totalMovies", moviePage.getTotalElements());
        model.addAttribute("totalPage", moviePage.getTotalPages());
        model.addAttribute("currentPage", pageBean.getPage());
        return "movie/movie-list-grid-fragment";
    }

    @GetMapping("/search/name/{name}")
    public String searchByName(@PathVariable("name") String name, Model model) throws MovieException, UserException {
        Movie movie = movieService.findByNameOne(name);
        if (movie == null || movie.getId() == null || movie.getId() == 0) {
            return "redirect:/movie/404";
        }
        return detail(movie.getId(), model);
    }

    @GetMapping("/movie-header")
    public void header(Model model) {
        String adminAccount = SecurityContextHolder.getContext().getAuthentication().getName();
        model.addAttribute("adminAccount", adminAccount);
    }

    /**
     * 获取默认图片
     *
     * @param resp
     */
    @GetMapping("/image")
    public void defaultImage(HttpServletResponse resp) {
        try {
            File file = ResourceUtils.getFile("classpath:static/assets/images/default.jpg");
            transferFile(resp, file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     * 通过response传输文件给客户端
     *
     * @param resp
     * @param file
     */
    private void transferFile(HttpServletResponse resp, File file) {
        if (file.exists()) {
            try (ServletOutputStream os = resp.getOutputStream(); FileInputStream fis = new FileInputStream(file)) {
                IOUtils.copy(fis, os);
            } catch (IOException ignored) {
            }
        }
    }

    @GetMapping("/rate")
    @ResponseBody
    public boolean rate(@Min(0) @Max(10) Integer rate, @Min(0) Integer id) throws MovieException, UserException {
        String account = SecurityContextHolder.getContext().getAuthentication().getName();
        cacheManager.getCache(MovieServiceImpl.CACHE_MOVIE_NAME).evict("movie_" + id);
        Movie movie = movieService.findById(id);
        movie.setRate(Float.valueOf(rate));
        movieService.save(movie);
        UserRate userRate = new UserRate();
        UserMovieId userMovieId = new UserMovieId();
        User user = userService.findByAccount(account)
                .orElseThrow(() -> new UserException(UserException.USER_CAN_NOT_FOUNT));
        userMovieId.setMovie(movie);
        userMovieId.setUser(user);
        userRate.setUserMovieId(userMovieId);
        userRate.setRate(Float.valueOf(rate));
        userRateService.save(userRate);
        return true;
    }

    @GetMapping("/query/condition")
    public String findByCondition(String movieName, Integer typeId, Float rate, Integer startYear, Integer endYear, PageBean pageBean, Model model) {
        Page<Movie> moviePage = movieService.findAllByCondition(movieName, typeId, rate, startYear, endYear, pageBean);
        model.addAttribute(moviePage.getContent());
        model.addAttribute("totalMovies", moviePage.getTotalElements());
        model.addAttribute("totalPage", moviePage.getTotalPages());
        model.addAttribute("currentPage", pageBean.getPage());
        return "movie/movie-list-grid-fragment";
    }

}